package com.ruoyi.project.system.user.controller;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.net.url.UrlQuery;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.shiro.authc.CustomizedToken;
import com.ruoyi.framework.shiro.enums.LoginType;
import com.ruoyi.framework.shiro.util.SignBuilder;
import com.ruoyi.framework.shiro.util.secret.MD5Util;
import com.ruoyi.framework.shiro.util.secret.SecurityUtil;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.project.system.third.controller.WebSocketServer;
import com.ruoyi.project.system.third.domain.SoapMessage;
import com.ruoyi.project.system.third.domain.Third;
import com.ruoyi.project.system.third.service.ThirdService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

/**
 * @author renzhihao
 * @date 2023/11/29 16:35
 */
@Controller
@RequestMapping("third")
public class ThirdLoginController extends BaseController {
    private static final Logger log = LoggerFactory.getLogger(ThirdLoginController.class);

    @Autowired
    private ThirdService thirdService;

    @Autowired
    private WebSocketServer webSocketServer;

    @GetMapping
    public void third(HttpServletResponse response) throws IOException {
        response.sendRedirect("login?way=third");
    }

    @ResponseBody
    @GetMapping("/getAuthPageUrl")
    public AjaxResult getAuthPageUrl() {

        Third third = thirdService.selectThirdAuth();

        String issueUrl;
        String issueType = third.getIssueType();
        String curUrl = third.getCurUrl();
        String requestId = IdUtil.fastUUID();

        Map<String, String> queryParam = new HashMap<>();

        // 渲染⽅式（0：单⻚⾯，1：IFrame， 默认单⻚⾯）
        queryParam.put("render", String.valueOf(third.getRender()));
        queryParam.put("issue", issueType);

        if (StringUtils.equals(issueType, "POST_MESSAGE")) {
            issueUrl = UrlBuilder.of(curUrl)
                    .addPath(third.getPostMessageIssueUrl())
                    .build();
        } else if (StringUtils.equals(issueType, "SOAP")) {
            issueUrl = UrlBuilder.of(curUrl)
                    .addPath(third.getSoapIssueUrl())
                    .build();
        } else {
            issueUrl = UrlBuilder.of(curUrl)
                    .addPath(third.getRedirectIssueUrl())
                    .build();
        }

        queryParam.put("issueUrl", issueUrl);


        queryParam.put("authType", third.getAuthType());
        queryParam.put("requestId", requestId);
        queryParam.put("clientId", third.getClientId());

        String sign = genSign(third.getClientSecret(), queryParam);
        queryParam.put("sign", sign);


        String authPageUrl = UrlBuilder.ofHttp(third.getAuthUrl())
                .setQuery(UrlQuery.of(queryParam, true))
                .build();

        log.info("url: {}", authPageUrl);

        Map<String, String> map = new HashMap<>();
        map.put("url", authPageUrl);
        map.put("issueType", issueType);
        map.put("wsUrl", UriComponentsBuilder.fromUriString(curUrl)
                .scheme("ws")
                .replacePath("soap")
                .pathSegment(requestId)
                .toUriString());

        map.put("render", String.valueOf(third.getRender()));
        return AjaxResult.success(map);
    }


    @ResponseBody
    @PostMapping("/login")
    public AjaxResult login(String code) {
        CustomizedToken token = new CustomizedToken(code, LoginType.THIRD);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
            return success();
        } catch (AuthenticationException e) {
            String msg = "获取用户失败";
            if (StringUtils.isNotEmpty(e.getMessage())) {
                msg = e.getMessage();
            }
            return error(msg);
        }
    }

    @ResponseBody
    @PostMapping("/soapLogin")
    public AjaxResult soapLogin(SoapMessage soapMessage) {
        boolean exist = webSocketServer.exist(soapMessage.getRequestId());
        if (!exist) {
            return error("当前请求" + soapMessage.getRequestId() + "不存在");
        }

        CustomizedToken token = new CustomizedToken(soapMessage.getAuthAccount(), JSONUtil.toBean(JSONUtil.toJsonStr(soapMessage), new TypeReference<>() {
        }, true), LoginType.THIRD);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
            return success();
        } catch (AuthenticationException e) {
            String msg = "获取用户失败";
            if (StringUtils.isNotEmpty(e.getMessage())) {
                msg = e.getMessage();
            }
            return error(msg);
        }
    }


    @ResponseBody
    @PostMapping("/soap")
    public AjaxResult soap(@RequestBody String body, HttpServletRequest request) {
        log.info("接收到IAM的SOAP信息，内容为：" + System.lineSeparator() + body);

        Third third = thirdService.selectThirdAuth();
        checkIAMRequest(request);
        String originBody = SecurityUtil.decryptAES(body, MD5Util.getMD5String(third.getClientSecret()));
        log.info("解密后的值为：" + System.lineSeparator() + originBody);

        SoapMessage soapMessage = JSONUtil.toBean(originBody, SoapMessage.class);
        webSocketServer.sendInfo(soapMessage.getRequestId(), originBody);

        return success();
    }

    public String genSign(String clientKey, Map<String, String> params) {
        Map<String, String> sortParams = new TreeMap<>(params);

        String paramStr = sortParams.values().stream()
                .map(String::trim)
                .collect(Collectors.joining());

        log.info("加密前原文：{}", paramStr + clientKey);

        return DigestUtils.md5DigestAsHex((paramStr + clientKey).getBytes(StandardCharsets.UTF_8));
    }

    private void checkIAMRequest(HttpServletRequest request) {
        Third third = thirdService.selectThirdAuth();
        Map<String, String[]> parameterMap = request.getParameterMap();

        SignBuilder signBuilder = SignBuilder.create(third.getClientId(), third.getClientSecret())
                .requestPath(request.getServletPath())
                .requestMethod(request.getMethod());

        parameterMap.forEach((key, value) -> {
            if (value != null && value.length > 0) {
                signBuilder.param(key, value[0]);
            }
        });

        String sign = signBuilder.sign();

        if (!StringUtils.equalsIgnoreCase(sign, request.getParameter("sign"))) {
            throw new AuthenticationException(String.format("签名校验失败, 提供的签名为: [%s]; 计算的签名为: [%s]; 计算签名的字符串为: [%s]",
                    request.getParameter("sign"), sign, signBuilder.getRawDataBeforeSign()));
        }
    }
}
